package com.rfid.transport;

import android.util.Log;

import com.rfid.api.GFunction;

import java.util.Arrays;

public class CommTransport {
    public static final int COMMTYPE_TCP = 1;
    public static final int COMMTYPE_COM = 2;
    public static final int COMMTYPE_BLUETOOTH = 3;
    public static final int COMMTYPE_HID = 4;
    public static final int IOCTRL_PURGECOMM = 1;
    public static final int IOCTRL_TERMINATE_WAIT = 2;
    public static final int IOCTRL_RESET_WAIT = 3;
    public static final int MINI_FRAME_SIZE = 8;
    protected static final int INPU_RAW_BUF_SIZE = 1024;
    private BufferPack m_lastdata = new BufferPack(1024);
    protected RingBuffer m_recvedBuffer = null;
    protected byte[] m_bufferForRead = null;
    protected IComm m_if = null;
    protected SockaddrAnSer m_remoteAddr = new SockaddrAnSer();

    public int connectBluetoolth(String BluetoolAddr) {
        this.m_if = new Transport_Bluetooth();
        Transport_Bluetooth m_bluetool = (Transport_Bluetooth) this.m_if;
        this.m_remoteAddr.setNad((byte) -1);
        int iret = m_bluetool.Connect(BluetoolAddr);
        if (iret == 0) {
            this.m_recvedBuffer = new RingBuffer(1024);
            this.m_bufferForRead = new byte[1024];
        }
        return iret;
    }

    public int connectCom(String sPath, int m_baud, String sFrame, byte addr) {
        this.m_if = new Transport_SerialPort();
        Transport_SerialPort m_com = (Transport_SerialPort) this.m_if;
        this.m_remoteAddr.setNad(addr);
        int iret = m_com.Connect(sPath, m_baud, sFrame);
        if (iret == 0) {
            this.m_recvedBuffer = new RingBuffer(1024);
            this.m_bufferForRead = new byte[1024];
        }
        return iret;
    }

    public int connectZTEK(int port, int baud, String frame, byte addr) {
        this.m_if = new Transport_Ztek();
        Transport_Ztek m_com = (Transport_Ztek) this.m_if;
        this.m_remoteAddr.setNad(addr);
        int iret = m_com.Connect(port, baud, frame);
        if (iret == 0) {
            this.m_recvedBuffer = new RingBuffer(1024);
            this.m_bufferForRead = new byte[1024];
        }
        return iret;
    }

    public int connectNetClent(String remoteIp, int remotePort) {
        this.m_if = new Transport_TcpClient();
        Transport_TcpClient m_tcp = (Transport_TcpClient) this.m_if;
        int iret = m_tcp.Connect(remoteIp, remotePort);
        if (iret == 0) {
            this.m_remoteAddr.setNad((byte) -1);
            this.m_recvedBuffer = new RingBuffer(1024);
            this.m_bufferForRead = new byte[1024];
        }
        return iret;
    }

    public int connectUsb(String usbDes) {
        this.m_if = new Transport_Hid();
        Transport_Hid m_hid = (Transport_Hid) this.m_if;
        int iret = m_hid.Connect(usbDes);
        if (iret == 0) {
            this.m_recvedBuffer = new RingBuffer(1024);
            this.m_bufferForRead = new byte[1024];
        }
        return iret;
    }

    public int getCommType() {
        if (this.m_if == null) return 0;
        if (this.m_if instanceof Transport_Bluetooth) return 3;
        if (this.m_if instanceof Transport_TcpClient) return 1;
        if (this.m_if instanceof Transport_SerialPort) return 2;
        if (this.m_if instanceof Transport_Hid) return 4;
        return 0;
    }

    public int disConnect() {
        if (this.m_if == null) return 2;
        int iret = this.m_if.DisConnect();
        if (iret == 0) this.m_if = null;
        return iret;
    }

    public int recv(byte[] buf, Integer len, int flags) {
        boolean truncated = false;
        int read_len = this.m_lastdata.readable_length();
        if (read_len > 0) {
            int iret;
            if (len.intValue() < read_len) {
                read_len = len.intValue();
                truncated = true;
            }
            this.m_lastdata.ReadData(buf, read_len);
            if (truncated) iret = -12;
            else iret = read_len;
            NdkSupport.SetIntegerValue(len, read_len);
            return iret;
        }
        this.m_lastdata.flush();
        int iret = ll_recv(this.m_lastdata);
        if (iret > 0) {
            read_len = this.m_lastdata.getBufferLen();
            if (len.intValue() < read_len) {
                read_len = len.intValue();
                truncated = true;
            }
            this.m_lastdata.ReadData(buf, read_len);
            if (truncated) iret = -12;
            else iret = read_len;
            NdkSupport.SetIntegerValue(len, read_len);
            return iret;
        }
        NdkSupport.SetIntegerValue(len, 0);
        return iret;
    }

    public int send(byte[] buf, int size, int flags) {
        return ll_send(buf, 0, size);
    }

    private BufferPack llSendframe = new BufferPack();

    protected int ll_send(byte[] buffer, int offset, int m_Size) {
        this.llSendframe.flush();
        this.llSendframe.WriteData((byte) 5);
        this.llSendframe.WriteData((byte) (4 + m_Size));
        this.llSendframe.WriteData(this.m_remoteAddr.getNad());
        this.llSendframe.WriteData(buffer, offset, m_Size);
        char crc16 = cal_crc16(this.llSendframe.GetBuffer(), 1, this.llSendframe.getBufferLen() - 1);
        this.llSendframe.WriteData(crc16);
        Log.d("SS:", GFunction.encodeHexStr(this.llSendframe.GetBuffer(),this.llSendframe.getBufferLen()));
        int ret = this.m_if.Send(this.llSendframe.GetBuffer(), 0, this.llSendframe.getBufferLen());
        if (ret != 0) return -2;
        return 0;
    }

    private BufferPack pllRecvFrame = new BufferPack();

    protected int ll_recv(BufferPack recvdata) {
        this.pllRecvFrame.flush();
        int ret = this.m_if.Receive(this.m_bufferForRead, 1024);
        if (ret > 0) this.m_recvedBuffer.PushData(this.m_bufferForRead, ret);
        else if (ret < 0) return -2;
        if (this.m_recvedBuffer.GetLength(-1, -1) > 0 && ParseFrame(this.pllRecvFrame) && this.pllRecvFrame.GetBuffer() != null) {
            if (this.m_remoteAddr.getNad() == this.pllRecvFrame.GetBuffer()[2] || this.m_remoteAddr.getNad() == -1)
                recvdata.WriteData(this.pllRecvFrame.GetBuffer(), 3, this.pllRecvFrame.getBufferLen() - 5);
            return this.pllRecvFrame.getBufferLen();
        }
        return 0;
    }

    private char cal_crc16(byte[] pCrcData, int offset, int pLen) {
        int CRC = 65535;
        int POLYNOMIAL = 33800;
        if (pLen == 0) return Character.MIN_VALUE;
        for (int i = 0; i < pLen; i++) {
            CRC ^= pCrcData[i + offset] & 0xFF;
            for (int j = 0; j < 8; j++)
                if ((CRC & 0x01) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else CRC >>= 1;
        }
        return (char) CRC;
    }

    public void ioctrol(int name) {
        switch (name) {
            case 1:
                this.m_if.ResetInputStream();
                this.m_recvedBuffer.Init();
                Arrays.fill(this.m_bufferForRead, (byte) 0);
                break;
        }
    }

    private boolean ParseFrame(BufferPack ppFrame) {
        int Poplen;
        boolean result = false;
        Frame_hdr hdr = new Frame_hdr();
        int startpos = -1;
        int CandidatePos = -1;
        while (true) {
            int ipos = this.m_recvedBuffer.SearchByte(startpos, (byte) 5);
            if (ipos == -1) break;
            int nLen = this.m_recvedBuffer.GetLength(ipos, -1);
            if (nLen < 8) {
                if (CandidatePos == -1) CandidatePos = ipos;
                break;
            }
            byte[] byTmp = new byte[6];
            this.m_recvedBuffer.GetBufData(ipos, byTmp.length, byTmp);
            hdr.setSOF(byTmp[0]);
            hdr.setLen(byTmp[1]);
            hdr.setAddr(byTmp[2]);
            hdr.setCommand((char) (byTmp[3] | byTmp[4] << 8));
            if (nLen < hdr.getLen() + 1) {
                if (CandidatePos == -1) CandidatePos = ipos;
                startpos = this.m_recvedBuffer.GetNewPos(ipos, 1);
                continue;
            }
            if (hdr.getLen() < 7) {
                startpos = this.m_recvedBuffer.GetNewPos(ipos, 1);
                continue;
            }
            byte[] Tmpbuf = new byte[hdr.getLen() + 1];
            this.m_recvedBuffer.GetBufData(ipos, hdr.getLen() + 1, Tmpbuf);
            char chksum = cal_crc16(Tmpbuf, 1, hdr.getLen() - 2);
            char chksum2 = (char) (Tmpbuf[1 + hdr.getLen() - 2] & 0xFF | Tmpbuf[1 + hdr.getLen() - 1] << 8 & 0xFF00);
            if (chksum != chksum2) {
                startpos = this.m_recvedBuffer.GetNewPos(ipos, 1);
                continue;
            }
            ppFrame.WriteData(Tmpbuf, 0, Tmpbuf.length);
            CandidatePos = this.m_recvedBuffer.GetNewPos(ipos, hdr.getLen() + 1);
            result = true;
            break;
        }
        if (CandidatePos == -1) Poplen = this.m_recvedBuffer.GetLength(-1, -1);
        else Poplen = this.m_recvedBuffer.GetLength(-1, CandidatePos);
        this.m_recvedBuffer.PopData(null, Poplen, null);
        return result;
    }
}
